package com.cti.ftpservice.dao.impl;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.log4j.Logger;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.grouping.AbstractAllGroupHeadsCollector;
import org.apache.lucene.search.grouping.GroupDocs;
import org.apache.lucene.search.grouping.SearchGroup;
import org.apache.lucene.search.grouping.TermAllGroupHeadsCollector;
import org.apache.lucene.search.grouping.TermAllGroupsCollector;
import org.apache.lucene.search.grouping.TermFirstPassGroupingCollector;
import org.apache.lucene.search.grouping.TermSecondPassGroupingCollector;
import org.apache.lucene.search.grouping.TopGroups;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.search.CachingCollector;
import org.apache.lucene.search.Collector;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MultiCollector;
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.Version;
import org.hibernate.CacheMode;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
import org.hibernate.search.SearchFactory;
import org.hibernate.search.batchindexing.MassIndexerProgressMonitor;
import org.hibernate.search.engine.metadata.impl.EmbeddedTypeMetadata;
import org.hibernate.search.engine.metadata.impl.PropertyMetadata;
import org.hibernate.search.engine.metadata.impl.TypeMetadata;
import org.hibernate.search.engine.spi.EntityIndexBinding;
import org.hibernate.search.impl.SimpleIndexingProgressMonitor;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.hibernate.search.spatial.SpatialFieldBridgeByRange;
import org.hibernate.search.spi.SearchFactoryIntegrator;
import org.hibernate.service.ServiceRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;

import com.cti.ftpservice.utils.FtpConfigInfo;
import com.cti.ftpservice.utils.HibernateUtil;
import com.cti.ftpservice.utils.SpringContextUtil;
import com.cti.ftpservice.utils.WsPropertiesLoadUtil;
import com.cti.ftpservice.vo.PageViewVO;
import com.ctibook.booksystem.vo.bookcatalog.BookInfoSearchNormalVO;
import com.ctibook.booksystem.vo.bookcatalog.BookInfoStatisticsTJVo;
import com.ctibook.booksystem.vo.bookcatalog.BookInfoStatisticsVo;

import javassist.compiler.ast.NewExpr;
/**
 * @author 鲁永刚 E-mail: luyg@ctibook.com
 * @version 创建时间：Sep 16, 2014 11:10:42 AM 类说明
 */
public class FullTextSearchDao extends BaseDao{
	private static Logger log = Logger.getLogger(FullTextSearchDao.class);
	
	@Autowired
	private FtpConfigInfo ftpConfigInfo;
	/**
	 * 获取当前FullTextSession
	 * @return
	 */
	protected FullTextSession getFullTextSession() {
		FullTextSession fullTextSession = Search.getFullTextSession(getSession());
		return fullTextSession;
	}

	/**
	 * 获取全文检索的QueryBuilder
	 * @param cls
	 * @return
	 */
	protected QueryBuilder getQueryBuilder(Class<?> cls) {
		FullTextSession fullTextSession = getFullTextSession();
		QueryBuilder queryBuilder = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity(cls).get();
		return queryBuilder;
	}
	
	protected Version getCurrentLuceneVersion() {
		return Version.LUCENE_36;
	}
	
	protected PropertyMetadata getPropertyMetadataByPropertyName(Class<?> cls,String propertyName) {
		PropertyMetadata pm=null;
		SearchFactory searchFactory=getFullTextSession().getSearchFactory();
		SearchFactoryIntegrator searchFactoryIntegrator=(SearchFactoryIntegrator)searchFactory;
		EntityIndexBinding entityIndexBinder=searchFactoryIntegrator.getIndexBinding(cls);
		TypeMetadata tm=entityIndexBinder.getDocumentBuilder().getMetadata();
		if(propertyName.contains(".")){
			List<EmbeddedTypeMetadata> ems=tm.getEmbeddedTypeMetadata();
			EmbeddedTypeMetadata embeddedTypeMetadata=null;
			String[] fieldNames=propertyName.split("\\.");
			for(int i=0;i<fieldNames.length-1;i++){
				String temp=fieldNames[i];
				for (EmbeddedTypeMetadata eMetadata : ems) {
					if(eMetadata.getEmbeddedFieldName().equals(temp)){
						embeddedTypeMetadata=eMetadata;
						ems=eMetadata.getEmbeddedTypeMetadata();
						break;
					}
				}
			}
			if(embeddedTypeMetadata==null)
				throw new RuntimeException("没有该属性："+propertyName);
			pm= embeddedTypeMetadata.getPropertyMetadataForProperty(fieldNames[fieldNames.length-1]);
			
		}else{
			pm=tm.getIdPropertyMetadata();
			if(pm==null){
				pm=tm.getPropertyMetadataForProperty(propertyName);
			}
		}
		return pm;
		
	}
	/**
	 * 获取类的分词器
	 * @param cls
	 * @return
	 */
	protected Analyzer getAnalyzerByClass(Class<?> cls) {
		return getFullTextSession().getSearchFactory().getAnalyzer(cls);
	}

	/**
	 * 根据名称获取分词器
	 * @param customerAnalyzerName
	 * @return
	 */
	protected Analyzer getCustomerAnalyzer(String customerAnalyzerName) {
		return getFullTextSession().getSearchFactory().getAnalyzer(customerAnalyzerName);
	}

	/**
	 * 获取当前所有的中文分词器
	 * @return
	 */
	protected Analyzer getCustomerAnalyzer() {
		return getCustomerAnalyzer("mmsegAnalyzer");
	}

	/**
	 * 无输入检索
	 * 
	 * @param <T> 泛型类型
	 * @param cls 查询类
	 * @param pageIndex 页所引
 	 * @param pageSize 页大小
	 * @return
	 */
	protected <T> PageViewVO<T> searchByBlank(Class<?> cls, int pageIndex, int pageSize,Class<?>... resultClzzs) {
		QueryBuilder queryBuilder = getQueryBuilder(cls);
		Query query = queryBuilder.keyword().wildcard().onField("id").matching("*").createQuery();
		return executeQuery(query, pageIndex, pageSize,resultClzzs);
	}

	/**
	 * 默认匹配
	 * 
	 * @param <T>
	 * @param cls
	 * @param propName
	 * @param searchText
	 * @param pageIndex
	 * @param pageSize
	 * @return
	 */
	protected <T> PageViewVO<T> searchByQueryString(Class<?> cls, String queryString, int pageIndex, int pageSize,Class<?>...resultClasses) {
		QueryParser queryParser = new QueryParser(getCurrentLuceneVersion(), "id", getCustomerAnalyzer());
		Query query;
		try {
			query = queryParser.parse(queryString);
			return executeQuery(query, pageIndex, pageSize,resultClasses);
		} catch (ParseException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 默认匹配
	 * 
	 * @param <T>
	 * @param cls
	 * @param propName
	 * @param searchText
	 * @param pageIndex
	 * @param pageSize
	 * @return
	 */
	protected <T> PageViewVO<T> searchByNormal(Class<?> cls, String propName, String searchText, int pageIndex, int pageSize,Class<?>...resultClass) {
		QueryBuilder queryBuilder = getQueryBuilder(cls);
		Query query = queryBuilder.keyword().onField(propName).matching(searchText).createQuery();
		return executeQuery(query, pageIndex, pageSize,resultClass);
	}

	/**
	 * 前驱匹配
	 * 
	 * @param <T>
	 * @param cls
	 * @param propName
	 * @param searchText
	 * @param pageIndex
	 * @param pageSize
	 * @return
	 */
	protected <T> PageViewVO<T> searchByPrefix(Class<?> cls, String propName, String searchText, int pageIndex, int pageSize,Class<?>...resultClass) {
		Query query = new PrefixQuery(new Term(propName, searchText));
		return executeQuery(query, pageIndex, pageSize,resultClass);
	}

	/**
	 * 模糊匹配
	 * 
	 * @param <T>
	 * @param cls
	 * @param propName
	 * @param searchText
	 * @param pageIndex
	 * @param pageSize
	 * @return
	 */
	protected <T> PageViewVO<T> searchByWildcard(Class<?> cls, String propName, String searchText, int pageIndex, int pageSize,Class<?>... resultClass) {
		QueryBuilder queryBuilder = getQueryBuilder(cls);
		Query query = queryBuilder.keyword().wildcard().onField(propName).matching(searchText).createQuery();
		return executeQuery(query, pageIndex, pageSize,resultClass);
	}

	/**
	 * 返回结果结合
	 * @param cls 查询的类
	 * @param queryString 查询字符串（符合lucene标准）
	 * @param propNames 要返回的列名称
	 * @param resultClass 将结果结合转换成的类型
	 * @return
	 */
	protected List<Object> searchByQueryStringAndProjectionProp(Class<?> cls, String queryString, String[] propNames,Class<?> resultClass) {
		QueryParser queryParser = new QueryParser(getCurrentLuceneVersion(), "id", getCustomerAnalyzer());
		Query query;
		try {
			query = queryParser.parse(queryString);
			return executeQueryAndProjectionProps(query, propNames,resultClass);
		} catch (ParseException e) {
			throw new RuntimeException(e);
		}
	}


	protected <T> PageViewVO<T> executeQuery(Query query, int pageIndex, int pageSize,Class<?>... resultClass) {
		return executeQuery(query,getCustomerAnalyzer(), pageIndex, pageSize,resultClass);
	}
	protected <T> PageViewVO<T> executeQuery(Query query,Analyzer analyzer,int pageIndex, int pageSize,Class<?>... resultClass) {
		return executeQuery(query, analyzer,null, pageIndex, pageSize,resultClass);
	}
	
	protected <T> PageViewVO<T> executeQuery(Query query,Sort sort,int pageIndex, int pageSize,Class<?>... resultClass) {
		return executeQuery(query, getCustomerAnalyzer(),sort, pageIndex, pageSize,resultClass);
	}
	
	@SuppressWarnings("unchecked")
	protected <T> PageViewVO<T> executeQuery(Query query,Analyzer analyzer,Sort sort, int pageIndex, int pageSize,Class<?>... resultClass) {
		FullTextSession fullTextSession = getFullTextSession();
		org.hibernate.search.FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(query,resultClass);
		if(sort!=null){
			fullTextQuery.setSort(sort);
		}
		int totalCount = fullTextQuery.getResultSize();
		int first = (pageIndex - 1) * pageSize;
		fullTextQuery.setFirstResult(first);
		fullTextQuery.setMaxResults(pageSize);
		List<T> datas = (List<T>) fullTextQuery.list();
		PageViewVO<T> pg = new PageViewVO<T>();
		pg.setCurrentPageIndex(pageIndex);
		pg.setPageRecordSize(pageSize);
		// 总记录数
		pg.setTotalRecordCount(totalCount);
		// 总页数
		int totalPage = (totalCount % pageSize) == 0 ? totalCount / pageSize : totalCount / pageSize + 1;
		totalPage = totalPage == 0 ? 1 : totalPage;
		pg.setTotalPageCount(totalPage);
		pg.setDatas(datas);
		Object[] objects = new Object[3];
		objects[0] = query;
		objects[1] = analyzer;
		pg.setAdditionalPara(objects);
		return pg;
	}

	@SuppressWarnings("unchecked")
	protected List<Object> executeQueryAndProjectionProps(Query query, String[] propNames,Class<?>... resuClasses) {
		FullTextSession fullTextSession = getFullTextSession();
		org.hibernate.search.FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(query,resuClasses);
		fullTextQuery.setProjection(propNames);
		@SuppressWarnings("rawtypes")
		List list = fullTextQuery.list();
		return list;
	}
	
	@SuppressWarnings("unchecked")
	protected List<Object> executeQueryAndProjectionProps(Query query, String[] propNames,String[] fullTextFilterName,Class<?>... resuClasses) {
		FullTextSession fullTextSession = getFullTextSession();
		org.hibernate.search.FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(query,resuClasses);
		if(fullTextFilterName!=null&&fullTextFilterName.length>0){
			for(String filterName:fullTextFilterName){
				fullTextQuery.enableFullTextFilter(filterName);
			}
		}
		fullTextQuery.setProjection(propNames);
		@SuppressWarnings("rawtypes")
		List list = fullTextQuery.list();
		return list;
	}
	
	//一条结果搜索
	@SuppressWarnings("unchecked")
	protected <T> T executeQueryForSingleObject(Query query,Class<?>... resultClass) {
		FullTextSession fullTextSession = getFullTextSession();
		org.hibernate.search.FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(query,resultClass);
		List<T> list = fullTextQuery.list();
		if(list.size()>0)
			return list.get(0);
		return null;
	}
	
	@SuppressWarnings("unchecked")
	protected <T> List<T> executeQueryForListObject(Query query,Sort sort,Class<?>... resultClass) {
		FullTextSession fullTextSession = getFullTextSession();
		org.hibernate.search.FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(query,resultClass);
		if(sort!=null){
			fullTextQuery.setSort(sort);
		}
		return fullTextQuery.list();
	}
	
	protected <T> List<T> executeQueryForListObject(Query query,Class<?>... resultClass) {
		return executeQueryForListObject(query,null,resultClass);
	}
	
	protected IndexSearcher getIndexSearcherByLucene(Class<?> resultClass){
		  String className=resultClass.getName();
		  Directory directory;
		  IndexSearcher indexSearcher=null;
		try {
			String ftpservicesystemIndex=ftpConfigInfo.getFtpservicesystemIndex();
			directory = FSDirectory.open(new File(ftpservicesystemIndex+className));
			indexSearcher=new IndexSearcher(IndexReader.open(directory));
		} catch (IOException e) {
			log.error(e.getMessage());
			e.printStackTrace();
		} 
		  return indexSearcher;
	}
	
	protected BookInfoStatisticsTJVo groupingByQuery(Query query,String groupingField,Sort groupSort, Class<?> resultClass) {
		BookInfoStatisticsTJVo tjVo=new BookInfoStatisticsTJVo();
		try {
			List<BookInfoStatisticsVo> vos=new ArrayList<BookInfoStatisticsVo>();	
			/** 前N条中分组 */
			int topNGroups = 10000;
			/** 分组起始偏移量 */
			int groupOffset = 0;
			/** 是否填充SearchGroup的sortValues */
			boolean fillFields = true;
			/** groupSort用于对组进行排序，docSort用于对组内记录进行排序，多数情况下两者是相同的，但也可不同 */
			Sort docSort = groupSort;
			/** 用于组内分页，起始偏移量 */
			int docOffset = 0;
			/** 每组返回多少条结果 */
			int docsPerGroup = 2;
			/** 是否需要计算总的分组数量 */
			boolean requiredTotalGroupCount = true;
			/** 是否需要缓存评分 */
			boolean cacheScores = true;
			//第一次查询缓存容量的大小：设置为16M
			double maxCacheRAMMB = 16;
			  TermFirstPassGroupingCollector c1 = new TermFirstPassGroupingCollector(groupingField, groupSort, groupOffset + topNGroups);
			  CachingCollector cachedCollector = CachingCollector.create(c1, cacheScores, maxCacheRAMMB);
			  IndexSearcher indexSearcher=getIndexSearcherByLucene(resultClass);
			  indexSearcher.search(query, cachedCollector);
			  Collection<SearchGroup<String>> topGroups = c1.getTopGroups(groupOffset, fillFields);
			  if (topGroups == null) {
				    // No groups matched
				    return null;
			  }
			  
			  Collector secondPassCollector = null;
			  boolean getScores = true;
			  boolean getMaxScores = true;
			// 如果需要对Lucene的score进行修正，则需要重载TermSecondPassGroupingCollector
			  TermSecondPassGroupingCollector c2 = new TermSecondPassGroupingCollector(groupingField, topGroups, groupSort, docSort, docOffset + docsPerGroup, getScores, getMaxScores, fillFields);

			// 如果需要计算总的分组数量，则需要把TermSecondPassGroupingCollector包装成TermAllGroupsCollector
			// TermAllGroupsCollector就是用来收集总分组数量的
			  TermAllGroupsCollector allGroupsCollector = null;
			  if (requiredTotalGroupCount) {
					allGroupsCollector = new TermAllGroupsCollector(groupingField);
					secondPassCollector = MultiCollector.wrap(c2, allGroupsCollector);
				} else {
					secondPassCollector = c2;
				}
			  
			  /**如果第一次查询已经加了缓存，则直接从缓存中取*/
			  if (cachedCollector.isCached()) {
					// 第二次查询直接从缓存中取
					cachedCollector.replay(secondPassCollector);
				} else {
					// 开始第二次分组查询
					indexSearcher.search(query, secondPassCollector);
				}
			  //Optionally compute total group count
		
			  /** 所有组的数量 */
				int totalGroupCount = 0;
				/** 所有满足条件的记录数 */
				int totalHitCount = 0;
				if (requiredTotalGroupCount) {
					totalGroupCount = allGroupsCollector.getGroupCount();
				}
			  
				TopGroups<String> groupsResult = c2.getTopGroups(docOffset);
				totalHitCount = groupsResult.totalHitCount;
				//totalGroupedHitCount = groupsResult.totalGroupedHitCount;
				tjVo.setTotalCount(totalHitCount);
				tjVo.setTotalGroupCount(totalGroupCount);
				for (GroupDocs<String> groupDocs : groupsResult.groups) {
					BookInfoStatisticsVo vo=new BookInfoStatisticsVo();
					vo.setStatisticsType(groupDocs.groupValue);
					vo.setCount(groupDocs.totalHits); //每组的总命中数
					vos.add(vo);
				} 
				tjVo.setStatisticsVos(vos);
			 return tjVo; 
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return tjVo;
	}
	
	protected String getQueryStringByStringList(List<String> list) {
		StringBuilder sb = new StringBuilder();
		for (String str : list) {
			sb.append("isbn: " + str + " ");
		}
		String result = sb.toString();
		result = result.trim();
		return result;
	}
	

	protected void rebuildIndex(Class<?> cls) {
		log.info("开始执行" + cls.getName() + "同步索引方法");
		MassIndexerProgressMonitor monitor = new SimpleIndexingProgressMonitor();
		try {
			getFullTextSession()//
					.createIndexer(cls)//
					.batchSizeToLoadObjects(10)//
					.cacheMode(CacheMode.NORMAL)//
					.threadsToLoadObjects(10)//
					.idFetchSize(10)//
					.progressMonitor(monitor)// 
					.startAndWait();
			log.info(cls.getName() + "同步索引方法执行完成");
		} catch (InterruptedException e) {
			log.error(cls.getName() + "同步索引方法执行出错：" + e.getMessage());
			e.printStackTrace();
		}
	}
}
